package self.hoy.demo.grainmall.product.service.impl;

import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import self.hoy.demo.common.constant.ProductConstant;
import self.hoy.demo.common.to.MemberPriceTo;
import self.hoy.demo.common.to.SkuReductionTo;
import self.hoy.demo.common.to.SpuBoundTo;
import self.hoy.demo.common.to.es.SkuHasStockVo;
import self.hoy.demo.common.to.es.SpuEsModel;
import self.hoy.demo.common.utils.PageUtils;
import self.hoy.demo.common.utils.Query;

import self.hoy.demo.common.utils.R;
import self.hoy.demo.grainmall.product.entity.*;
import self.hoy.demo.grainmall.product.feign.CouponFeignService;
import self.hoy.demo.grainmall.product.feign.SearchFeignService;
import self.hoy.demo.grainmall.product.feign.WareFeignService;
import self.hoy.demo.grainmall.product.mapper.SpuInfoMapper;
import self.hoy.demo.grainmall.product.service.*;
import self.hoy.demo.grainmall.product.vo.*;
import self.hoy.demo.grainmall.product.vo.Attr;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoMapper, SpuInfo> implements SpuInfoService {

    @Autowired
    private SpuInfoDescService spuInfoDescService;

    @Autowired
    private SpuImagesService spuImagesService;

    @Autowired
    private SkuInfoService skuInfoService;

    @Autowired
    private AttrService attrService;

    @Autowired
    private ProductAttrValueService attrValueService;

    @Autowired
    private SkuImagesService skuImagesService;

    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private SearchFeignService searchFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SpuInfo> page = this.page(
                new Query<SpuInfo>().getPage(params),
                new QueryWrapper<SpuInfo>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageByCondition(Map<String, Object> params) {
        QueryWrapper<SpuInfo> wrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        if (!StringUtils.isEmpty(key)) {
            wrapper.and(w -> w.eq("id", key).or().like("spu_name", key));
        }
        String status = (String)params.get("status");
        if (!StringUtils.isEmpty(key)) {
            wrapper.eq("publish_status", status);
        }
        String brandId = (String)params.get("brandId");
        if (!StringUtils.isEmpty(brandId)) {
            wrapper.eq("brand_id", brandId);
        }
        String catelogId = (String)params.get("catelogId");
        if (!StringUtils.isEmpty(key)) {
            wrapper.eq("catelog_id", catelogId);
        }

        IPage<SpuInfo> page = this.page(
                new Query<SpuInfo>().getPage(params), wrapper
        );

        return new PageUtils(page);
    }

    @Override
    @Transactional
    public void up(Long spuId) {
        List<SkuInfo> skus = skuInfoService.getSkusBySpuId(spuId);
        List<ProductAttrValue> attrValues = attrValueService.baseAttrListForSpu(spuId);
        List<Long> attrIds = attrValues.stream().map(attr -> attr.getAttrId()).collect(Collectors.toList());
        attrIds = attrService.selectSearchAttrIds(attrIds);

        Set<Long> attrIdSet = new HashSet<>(attrIds);
        List<SpuEsModel.Attrs> attrs = attrValues.stream().filter(attr -> attrIdSet.contains(attr.getAttrId())).map(a -> {
            SpuEsModel.Attrs ma = new SpuEsModel.Attrs();
            ma.setAttrId(a.getAttrId());
            ma.setAttrName(a.getAttrName());
            ma.setAttrValue(a.getAttrValue());
            return ma;
        }).collect(Collectors.toList());

        List<Long> list = skus.stream().map(SkuInfo::getSkuId).collect(Collectors.toList());

        List<SkuHasStockVo> hasStockVos = null;
        try {            // 发送远程调用，库存系统查询是否有库存
            R result = wareFeignService.getSkusHasStock(list);
            hasStockVos = result.getData(new TypeReference<List<SkuHasStockVo>>(){});
        } catch (Exception e) {
//            stockVoMap = new HashMap<>();
        }
        Map<Long, Boolean> stockVoMap = hasStockVos == null ?
                new HashMap<>() :
                hasStockVos.stream().collect(
                        Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));

        List<SpuEsModel> models = skus.stream().map(sku -> {
            SpuEsModel model = new SpuEsModel();
            BeanUtils.copyProperties(sku, model);
            model.setSkuPrice(sku.getPrice());
            model.setSkuImg(sku.getSkuDefaultImg());

            model.setHasStock(stockVoMap.get(sku.getSkuId()));
            model.setHotScore(0L);

            Brand brand = brandService.getById(sku.getBrandId());
            model.setBrandImg(brand.getLogo());
            model.setBrandName(brand.getName());
            Category category = categoryService.getById(sku.getCatalogId());
            model.setCatalogName(category.getName());
            model.setAttrs(attrs);
            return model;
        }).collect(Collectors.toList());

        R result = searchFeignService.productStausUp(models);
        if (result.getCode() == 0) {
            baseMapper.updateSpuStatus(spuId, ProductConstant.SPU_STATUS.UP.getCode());
        } else {
            //TODO 重复调用？接口幂等性，重试机制？
        }
    }

    @Override
    @Transactional
    public void saveSpuInfo(SpuSaveVo vo) {
        // 1、保存spu基本信息 pms_spu_info
        SpuInfo info = new SpuInfo();
        BeanUtils.copyProperties(vo, info);
        info.setCreateTime(new Date());
        info.setUpdateTime(info.getCreateTime());
        info.setPublishStatus(vo.getPublishStatus());
        this.saveBaseSpuInfo(info);//TODO 分布式自增主键

        // 2、保存spu的描述图片 pms_spu_info_desc
        List<String> decript = vo.getDecript();
        SpuInfoDesc desc = new SpuInfoDesc();
        desc.setSpuId(info.getId());
        desc.setDecript(String.join(",", decript));
        spuInfoDescService.saveSpuInfoDesc(desc);

        // 3、保存spu的图片集
        List<String> images = vo.getImages();
        spuImagesService.saveImages(info.getId(), images);

        // 4、保存spu的规格参数 pms_product_attr_value
        List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
        List<ProductAttrValue> productAttrValues = baseAttrs.stream().map(a -> {
            ProductAttrValue v = new ProductAttrValue();
            self.hoy.demo.grainmall.product.entity.Attr attr = attrService.getById(a.getAttrId());
            v.setSpuId(info.getId());
            v.setAttrId(a.getAttrId());
            v.setAttrName(attr.getAttrName());
            v.setAttrValue(a.getAttrValues());
            v.setQuickShow(a.getShowDesc());
            return v;
        }).collect(Collectors.toList());
//        attrValueService.saveBatch(productAttrValues);
        attrValueService.saveProductAttr(productAttrValues);//Question: 这里为什么不直接调用 saveBatch ?

        // 5、保存spu的积分信息（跨库）
        Bounds bounds = vo.getBounds();
        SpuBoundTo spuBoundTo = new SpuBoundTo();
        spuBoundTo.setSpuId(info.getId());
        spuBoundTo.setBuyBounds(bounds.getBuyBounds());
        spuBoundTo.setGrowBounds(bounds.getGrowBounds());
        couponFeignService.saveSpuBounds(spuBoundTo);

        // 6、保存当前spu对应的所有sku信息
        List<Skus> skus = vo.getSkus();
        if (skus != null && skus.size() > 0) {
            skus.forEach(item -> {
                SkuInfo s = new SkuInfo();
                BeanUtils.copyProperties(item, s);
                s.setBrandId(info.getBrandId());
                s.setCatalogId(info.getCatalogId());
                s.setSpuId(info.getId());
                s.setSaleCount(0L);
                for (Images img : item.getImages()) {
                    if (img.getDefaultImg() == 1) {
                        s.setSkuDefaultImg(img.getImgUrl());
                    }
                }
        // 6.1、sku的基本信息、pms_sku_info
                skuInfoService.saveSkuInfo(s);

        // 6.2、sku的图片信息 pms_sku_image
                List<SkuImages> imagesList = item.getImages().stream().filter(img->{
                    return !StringUtils.isEmpty(img.getImgUrl());
                }).map(img -> {
                    SkuImages skuImages = new SkuImages();
                    skuImages.setSkuId(s.getSkuId());
                    skuImages.setImgUrl(img.getImgUrl());
                    skuImages.setDefaultImg(img.getDefaultImg());
                    return skuImages;
                }).collect(Collectors.toList());
                skuImagesService.saveBatch(imagesList);

                List<Attr> attr = item.getAttr();
                List<SkuSaleAttrValue> skuSaleAttrValues = attr.stream().map(a -> {
                    SkuSaleAttrValue attrValue = new SkuSaleAttrValue();
                    BeanUtils.copyProperties(a, attrValue);
                    attrValue.setSkuId(s.getSkuId());
                    return attrValue;
                }).collect(Collectors.toList());
                skuSaleAttrValueService.saveBatch(skuSaleAttrValues);

//                保存sku的优惠、满减等信息
                SkuReductionTo to = new SkuReductionTo();
                if (item.getFullCount() > 0 || item.getFullPrice().compareTo(BigDecimal.ZERO) > 0) {
                    BeanUtils.copyProperties(item, to);
                    to.setMemberPrice(
                            item.getMemberPrice().stream().map(p -> {
                                MemberPriceTo mpt = new MemberPriceTo();
                                BeanUtils.copyProperties(p, mpt);
                                return mpt;
                            }).collect(Collectors.toList())
                    );
                    to.setSkuId(s.getSkuId());
                    couponFeignService.saveSkuReduction(to);
                }
            });
        }
    }

    private void saveBaseSpuInfo(SpuInfo info) {
        baseMapper.insert(info);
    }

}